﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;

namespace Majoopic.Utility.Image
{
    public class BlockImageReader : BaseImageReader
    {
        //protected BlockExifHeaderReader exifReader;

        protected BinaryReader reader;
        
        /// <summary>
        /// Indicates whether to read data using big or little endian byte aligns
        /// </summary>
        protected bool isLittleEndian = false;
        protected long tiffHeaderStart;


        //protected static readonly Regex nullDateTimeMatcher = new Regex(@"^[\s0]{4}[:\s][\s0]{2}[:\s][\s0]{5}[:\s][\s0]{2}[:\s][\s0]{2}$");
        

        #region Constructor
        public BlockImageReader(string fileName)
            : base(fileName)
        {

        }

        public BlockImageReader(Stream fileStream)
            : base(fileStream)
        {            
            
        }
        #endregion

        ///// <summary>
        ///// Get the ExifInformation of the current stream
        ///// </summary>
        ///// <returns>Exif information</returns>
        //public ImageHeaderInformation GetImageInformation()
        //{
        //    ImageHeaderInformation imageInformation = new ImageHeaderInformation();
        //    //this.SeekToExifTag();

        //    //this.FillExifInformation(imageInformation);

        //    return imageInformation;
        //}
               

        #region Methods for reading data directly from the filestream (From the library ExifLib)

        /// <summary>
        /// Gets a 2 byte unsigned integer from the file
        /// </summary>
        /// <returns></returns>
        protected ushort ReadUShort()
        {
            return ToUShort(ReadBytes(2));
        }

        /// <summary>
        /// Gets a 4 byte unsigned integer from the file
        /// </summary>
        /// <returns></returns>
        protected uint ReadUint()
        {
            return ToUint(ReadBytes(4));
        }

        protected string ReadString(int chars)
        {
            var bytes = ReadBytes(chars);
            return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
        }

        protected byte[] ReadBytes(int byteCount)
        {
            return this.reader.ReadBytes(byteCount);
        }

        /// <summary>
        /// Reads some bytes from the specified TIFF offset
        /// </summary>
        /// <param name="tiffOffset"></param>
        /// <param name="byteCount"></param>
        /// <returns></returns>
        protected byte[] ReadBytes(ushort tiffOffset, int byteCount)
        {
            // Keep the current file offset
            long originalOffset = this.ImageStream.Position;

            // Move to the TIFF offset and retrieve the data
            this.ImageStream.Seek(tiffOffset + tiffHeaderStart, SeekOrigin.Begin);

            byte[] data = this.reader.ReadBytes(byteCount);

            // Restore the file offset
            this.ImageStream.Position = originalOffset;

            return data;
        }

        #endregion

        #region Data conversion (From the library ExifLib)
        /// <summary>
        /// Converts 2 bytes to a ushort using the current byte aligns
        /// </summary>
        /// <returns></returns>
        protected ushort ToUShort(byte[] data)
        {
            if (isLittleEndian != BitConverter.IsLittleEndian)
                Array.Reverse(data);

            return BitConverter.ToUInt16(data, 0);
        }

        /// <summary>
        /// Converts 8 bytes to an unsigned rational using the current byte aligns.
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        /// <seealso cref="ToRational"/>
        protected double ToURational(byte[] data)
        {
            var numeratorData = new byte[4];
            var denominatorData = new byte[4];

            Array.Copy(data, numeratorData, 4);
            Array.Copy(data, 4, denominatorData, 0, 4);

            uint numerator = ToUint(numeratorData);
            uint denominator = ToUint(denominatorData);

            return numerator / (double)denominator;
        }

        /// <summary>
        /// Converts 8 bytes to a signed rational using the current byte aligns.
        /// </summary>
        /// <remarks>
        /// A TIFF rational contains 2 4-byte integers, the first of which is
        /// the numerator, and the second of which is the denominator.
        /// </remarks>
        /// <param name="data"></param>
        /// <returns></returns>
        protected double ToRational(byte[] data)
        {
            var numeratorData = new byte[4];
            var denominatorData = new byte[4];

            Array.Copy(data, numeratorData, 4);
            Array.Copy(data, 4, denominatorData, 0, 4);

            int numerator = ToInt(numeratorData);
            int denominator = ToInt(denominatorData);

            return numerator / (double)denominator;
        }

        /// <summary>
        /// Converts 4 bytes to a uint using the current byte aligns
        /// </summary>
        /// <returns></returns>
        protected uint ToUint(byte[] data)
        {
            if (isLittleEndian != BitConverter.IsLittleEndian)
                Array.Reverse(data);

            return BitConverter.ToUInt32(data, 0);
        }

        /// <summary>
        /// Converts 4 bytes to an int using the current byte aligns
        /// </summary>
        /// <returns></returns>
        protected int ToInt(byte[] data)
        {
            if (isLittleEndian != BitConverter.IsLittleEndian)
                Array.Reverse(data);

            return BitConverter.ToInt32(data, 0);
        }

        protected double ToDouble(byte[] data)
        {
            if (isLittleEndian != BitConverter.IsLittleEndian)
                Array.Reverse(data);

            return BitConverter.ToDouble(data, 0);
        }

        protected float ToSingle(byte[] data)
        {
            if (isLittleEndian != BitConverter.IsLittleEndian)
                Array.Reverse(data);

            return BitConverter.ToSingle(data, 0);
        }

        protected short ToShort(byte[] data)
        {
            if (isLittleEndian != BitConverter.IsLittleEndian)
                Array.Reverse(data);

            return BitConverter.ToInt16(data, 0);
        }

        protected sbyte ToSByte(byte[] data)
        {
            // An sbyte should just be a byte with an offset range.
            return (sbyte)(data[0] - byte.MaxValue);
        }
        #endregion        
    
        public override void FillImageHeaderInformation(ImageHeaderInformation headerInformation)
        {
            throw new NotImplementedException();
        }

        public override void Dispose()
        {
            
            base.Dispose();
        }
    }
}
